﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Magnet.Engine;

namespace Magnet.Engine.Entities
{
    public class Entity
    {
        #region Normal Fields
        protected Vector2 position;
        protected Vector2 size;
        protected Vector2 velocity;
        protected Texture2D texture;
        protected float rotation;
        protected SpriteEffects spriteEffect = SpriteEffects.None;
        protected Color tint = Color.White;

        protected float depth;
        protected Vector2 origin;
        protected float velocityBound = 2.0f;
        protected float velocityApplicationBound = 0.5f;

        //Marked for removal
        protected bool isMarkedForRemoval;
        #endregion
        #region Animation Fields
        //Frame animation
        protected int resetFrame;
        protected int startFrame;
        protected int stopFrame;
        protected int frameCount = 1;
        protected int frameRow = 0;
        protected float FPS;
        protected float totalElapsed;
        protected bool isAnimating;
        protected bool isShowing = true;
        protected bool loop = true;
        protected int loopCounter = 0;

        //flicker
        const float FLICKER_FREQUENCY = 10f;
        protected bool visible;
        protected bool flicker;
        protected double nextFlickerUpdate;

        //scale
        protected float scale = 1.0f;
        protected float scaleTime;
        protected float scalePerSecond;
        #endregion

        #region Normal Properties
        public Texture2D Texture
        {
            get { return this.texture; }
            set { this.texture = value; }
        }
        public Vector2 Position
        {
            get { return this.position; }
            set { this.position = value; }
        }
        public Vector2 Size
        {
            get { return this.size; }
            set { this.size = value; }
        }
        public Vector2 Velocity
        {
            get { return this.velocity; }
            set { this.velocity = value; }
        }
        public float Rotation
        {
            get { return this.rotation; }
            set { this.rotation = value; }
        }
        public float Depth
        {
            get { return this.depth; }
            set { this.depth = value; }
        }
        public SpriteEffects SpriteEffect
        {
            get { return this.spriteEffect; }
            set { this.spriteEffect = value; }
        }
        public Vector2 Origin
        {
            get { return new Vector2((int)this.size.X / 2, (int)this.size.Y / 2); }
            set { this.origin = value; }
        }

        public int Radius
        {
            get { return this.texture.Width / 2; }
        }
        public bool IsMarkedForRemoval
        {
            get { return isMarkedForRemoval; }
            set { this.isMarkedForRemoval = value; }
        }
        #endregion
        #region Animation Properties
        public bool IsShowing
        {
            get { return this.isShowing; }
            set { this.isShowing = value; }
        }
        public bool IsAnimating
        {
            get { return this.isAnimating; }
            set { this.isAnimating = value; }
        }
        public bool Loop
        {
            get { return this.loop; }
            set { this.loop = value; }
        }
        public int LoopCounter
        {
            get { return this.loopCounter; }
            set { this.loopCounter = value; }
        }
        public bool Flicker
        {
            set
            {
                this.flicker = value;
                this.visible = !value;
            }
        }
        public float Scale
        {
            get { return this.scale; }
            set { this.scale = value; }
        }
        public bool IsScaling
        {
            get { return this.scaleTime > 0f; }
        }
        #endregion
        
        public Entity(Vector2 position, Vector2 size, String texture)
        {
            this.position = position;
            this.size = size;
            if (texture != null)
                this.texture = TextureManager.LoadTexture(texture);
            depth = 0.0f;
            origin = new Vector2(size.X / 2, size.Y / 2);
            size = new Vector2();
        }
        public virtual void LoadContent(){}

        #region Update & Draw
        public virtual void Update(float delta)
        {
            //Move the Entity.
            Move(delta);

            float elapsed = delta;
            int total = (int)delta;

            if (this.isAnimating)
            {
                this.totalElapsed += elapsed;
                if (this.totalElapsed > this.FPS)
                {
                    this.startFrame++;
                    // Keep the Frame between 0 and the total frames, minus one.
                    this.startFrame %= this.stopFrame;

                    if (this.startFrame <= resetFrame)
                    {
                        this.startFrame = resetFrame;
                    }

                    this.totalElapsed -= this.FPS;

                    if (!loop)
                    {
                        loopCounter++;
                        if (loopCounter == stopFrame - 1)
                        {
                            this.startFrame = 0;
                            loopCounter = stopFrame - 1;
                            isAnimating = false;
                            isShowing = false;
                        }
                    }
                }
            }

            if (this.flicker)
            {
                if (total > nextFlickerUpdate)
                {
                    this.visible = !this.visible;
                    this.nextFlickerUpdate = total + 1 / FLICKER_FREQUENCY;
                }
            }

            if (this.IsScaling)
            {
                this.scale += this.scalePerSecond * elapsed;
                this.scaleTime -= elapsed;
            }
        }

        public virtual void Draw(SpriteBatch spriteBatch)
        {
            Rectangle stop = new Rectangle(((int)this.size.X * this.stopFrame), (int)this.size.Y * this.frameRow, (int)this.size.X, (int)this.size.Y);

            if (this.isAnimating && isShowing)
            {
                if (this.startFrame < this.stopFrame)
                {
                    Rectangle start = new Rectangle(((int)this.size.X * this.startFrame), (int)this.size.Y * this.frameRow, (int)this.size.X, (int)this.size.Y);
                    spriteBatch.Draw(this.texture, this.position, start, this.tint, this.rotation, this.Origin, this.scale, spriteEffect, this.depth);
                }
            }
            if (!this.isAnimating && isShowing)
            {
                spriteBatch.Draw(this.texture, this.position, stop, this.tint, this.rotation, this.origin, this.scale, spriteEffect, this.depth);
            }
        }
        #endregion
        #region Animation
        public void StartScale(float targetScale, float scaleTime)
        {
            if (this.IsScaling)
                return;

            this.scaleTime = scaleTime;
            this.scalePerSecond = (targetScale - this.scale) / scaleTime;
        }
        #endregion
        #region Utilities

        /// <summary>
        /// Uses simple bounding box collision to tell whether or not this Entity is colliding with another.
        /// </summary>
        /// <param name="e">The Entity we're checking for.</param>
        /// <returns>Whether or not their bounding boxes intersect each other.</returns>
        public bool Intersects(Entity e)
        {
            Vector2 dist = e.Position - position;
            return (dist.Length() <= size.Length()/2.5f + e.Size.Length()/2.5f);

            /*return (position.X + origin.X >= e.Position.X - e.Origin.X
                 && position.X - origin.X <= e.Position.X + e.Origin.X
                 && position.Y + origin.Y >= e.Position.Y - e.Origin.Y
                 && position.Y - origin.Y <= e.Position.Y + e.Origin.Y);*/
        }

        public bool Intersects(Vector2 v)
        {
            Vector2 dist = v - position;
            return (dist.Length() <= size.Length()/2.5f);

           /* return (position.X + origin.X >= v.X
                 && position.X - origin.X <= v.X
                 && position.Y + origin.Y >= v.Y
                 && position.Y - origin.Y <= v.Y);*/
        }

        public void ApplyVelocity(Vector2 vel)
        {
            //Apply a clamped velocity.
            velocity.X += MathHelper.Clamp(vel.X, -velocityApplicationBound, velocityApplicationBound);
            velocity.Y += MathHelper.Clamp(vel.X, -velocityApplicationBound, velocityApplicationBound);
        }

        //Uses the velocity to move.
        protected virtual void Move(float delta)
        {
            //Clamp the velocity.
            velocity.X = MathHelper.Clamp(velocity.X, -velocityBound, velocityBound);
            velocity.Y = MathHelper.Clamp(velocity.Y, -velocityBound, velocityBound);

            //Apply the velocity.
            float d = delta / World.NORMAL_TIME_CHANGE;
            position.X += velocity.X * d;
            position.Y += velocity.Y * d;
        }

        #endregion

    }
}
